#include <QPainter>
#include <QPen>
#include <QFont>
#include "tetrix.h"
#include "ui_tetrix.h"
#include <string.h>

Tetrix::Tetrix(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Tetrix),_rt(RT_ROW,RT_COL),
    _semaphore(0)
{
    ui->setupUi(this);

    this->setMinimumSize(640,410 + this->menuBar()->height());
    this->setMaximumSize(640,410 + this->menuBar()->height());

    this->ui->centralWidget->setMinimumSize(640,400);
    this->ui->centralWidget->setMaximumSize(640,400);

    _game_level = 1;
    _response.reset(0);;
}

Tetrix::~Tetrix()
{
    delete ui;
}

bool Tetrix::start()
{
    char path[1024] = {0};
    ACE_OS::getcwd(path,1024);
    std::string cur_path(path);
    cur_path += '/';
    cur_path += CONFIG_FILE;

    IniParser ini(cur_path);
    std::string sIP = ini.get(CONFIG_SECTION_SERVER,CONFIG_SECTION_SERVER_IP);
    std::string sPort = ini.get(CONFIG_SECTION_SERVER,CONFIG_SECTION_SERVER_PORT);

    if(sIP.length() && sPort.length()){
        ACE_INET_Addr server_addr;
        if(server_addr.set(atoi(sPort.data()),sIP.data()) == -1){
            ACE_DEBUG((LM_DEBUG,"Bad Server Config(%s:%s)\n",sIP.c_str(),sPort.c_str()));
            _conn.set_handle(0);
        }
        else{
            ACE_SOCK_Connector connector;
            if(connector.connect(_conn,server_addr) == -1){
                _conn.set_handle(0);
                ACE_DEBUG((LM_DEBUG,"Connect Server Fail(%s)\n",strerror(errno)));
            }
            else{
                ACE_DEBUG((LM_DEBUG,"Connected To Server(%s:%d)\n",server_addr.get_host_addr(),server_addr.get_port_number()));

                if(ACE_Thread::spawn((ACE_THR_FUNC)Tetrix::worker,(void *)this,THR_DETACHED|THR_NEW_LWP,&_worker_id) != 0){
                    ACE_DEBUG((LM_DEBUG,"Start Worker Thread Fail(%s)\n",strerror(errno)));
                    _conn.close();
                    _conn.set_handle(0);
                    return false;
                }

                ACE_DEBUG((LM_DEBUG,"Start Worker Thread OK\n"));

                on_action_Room_List_triggered();
                return true;
            }
        }
    }
    else{
        ACE_DEBUG((LM_DEBUG,"Read Config File Fail\n"));
    }

    return false;
}

void Tetrix::restartTimer()
{
    _timer.stop();
    _timer.start(TIMER_TIMEOUT/_game_level,this);
}

void Tetrix::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void Tetrix::updateRoomList()
{
    this->ui->roomList->clear();
    if(this->_room_map.size()){
        GetRoomListResp::RoomMap::iterator iter;
        for(iter = _room_map.begin(); iter != _room_map.end(); iter++){
            std::string room_name = iter->first;

            quint8 member_cnt = iter->second.first;
            bool   all_ready = iter->second.second;

            char row[1024] = {0};
            sprintf(row,"%s(%d:%s)",room_name.c_str(),member_cnt,all_ready ? "Running" : "Waiting");
            this->ui->roomList->addItem(QString(row));
        }
    }
}

void Tetrix::paintEvent(QPaintEvent *event)
{
    this->ui->score->setNumDigits(int(_rt.getRemovedLine()));

    updateRoomList();

    QPainter painter(this);
    _rt.draw(painter,QPoint(0,this->ui->menuBar->height()));

    if(_rt.isGameOver()){
        painter.setPen(Qt::red);
        QFont font = painter.font();
        font.setPointSize(20);
        painter.setFont(font);
        painter.drawText(QPoint((RT_COL/2 - 3) * RUSSIA_TETRIX_SIZE,RT_ROW/2 * RUSSIA_TETRIX_SIZE),"Game Over");
        this->gameOver();
    }

    QMainWindow::paintEvent(event);
}


void Tetrix::timerEvent(QTimerEvent *event)
{
    if(event->timerId() == _timer.timerId()){
        if(_rt.isRunning())
        {
            if(!_rt.dropDown() && _rt.isRunning())
            {
                _rt.handelCollision();
                _rt.removeCompletedLine();
            }
            update();
        }
    }
}

void Tetrix::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()){
        case Qt::Key_Left:{
            if(_rt.isRunning())
            {
                if(_rt.moveLeft())
                    update();

                if(_rt.isCollision(Down))
                {
                    _rt.handelCollision();
                    _rt.removeCompletedLine();
                    update();
                }
            }
            break;
        }
        case Qt::Key_Right:{
            if(_rt.isRunning())
            {
                if(_rt.moveRight())
                    update();

                if(_rt.isCollision(Down))
                {
                    _rt.handelCollision();
                    _rt.removeCompletedLine();
                    update();
                }
            }
            break;
        }
        case Qt::Key_Down:{
            if(_rt.isRunning())
            {
                if(!_rt.dropDown())
                {
                    _rt.handelCollision();
                    _rt.removeCompletedLine();
                }
                update();

                restartTimer();
            }
            break;
        }
        case Qt::Key_Up:{
            if(_rt.isRunning())
            {
                if(_rt.rotateLeft())
                    update();
            }
            break;
       }

        default:
            QMainWindow::keyPressEvent(event);
        }
}

void Tetrix::on_actionStartGame_triggered()
{
    RussiaTetrix rt(RT_ROW,RT_COL);
    _rt = rt;

    _rt.changePiece();

    _timer.stop();

    _timer.start(TIMER_TIMEOUT/_game_level,this);

    _rt.setGameStatus(RUNNING);
}

void Tetrix::on_actionExit_triggered()
{
    on_action_Leave_Room_triggered();
    QApplication::exit();
}

void Tetrix::on_actionLevel_1_triggered()
{
    _game_level = 1;
    on_actionStartGame_triggered();
}

void Tetrix::on_actionLevel_2_triggered()
{
    if(_game_level != 2){
        _game_level = 2;
        on_actionStartGame_triggered();
    }
}

void Tetrix::on_actionLevel_3_triggered()
{
    if(_game_level != 3){
        _game_level = 3;
        on_actionStartGame_triggered();
    }
}

void Tetrix::on_actionLevel_4_triggered()
{
    if(_game_level != 4){
        _game_level = 4;
        on_actionStartGame_triggered();
    }
}

void Tetrix::on_actionLevel_5_triggered()
{
    if(_game_level != 5){
        _game_level = 5;
        on_actionStartGame_triggered();
    }
}

void Tetrix::on_actionLevel_6_triggered()
{
    if(_game_level != 6){
        _game_level = 6;
        on_actionStartGame_triggered();
    }
}

void Tetrix::gameOver()
{
    _rt.setGameStatus(GAMEOVER);
    _timer.stop();
}

bool Tetrix::doRequest(ProtoSrv& request)
{
    ProtoSrv *pReq = &request;

    ACE_OutputCDR cdr;
    if(!ProtoSrvManager::getInstance()->encode(cdr,&request)){
        ACE_DEBUG((LM_DEBUG,"Make Request Fail(%d:%s)\n",request.getProtocolType(),typeid(*pReq).name()));
        return false;
    }

    ACE_DEBUG((LM_DEBUG,"Made Request(%d:%s)\n",request.getProtocolType(),typeid(*pReq).name()));

    ssize_t sent = _conn.send_n(cdr.begin());
    if(sent != (ssize_t)cdr.total_length()){
        ACE_DEBUG((LM_DEBUG,"Send Request Fail(%s)\n",strerror(errno)));
        return false;
    }

    ACE_DEBUG((LM_DEBUG,"Sent Request(%d:%s)\n",request.getProtocolType(),typeid(*pReq).name()));

    ACE_Time_Value timeout = ACE_OS::gettimeofday();
    timeout.sec(timeout.sec() + WAIT_RESPONSE_TIMEOUT_SEC);

    if(_semaphore.acquire(timeout) == -1){
        ACE_DEBUG((LM_DEBUG,"Recv Response Fail(%s)\n",strerror(errno)));
    }
    else{
        ACE_DEBUG((LM_DEBUG,"Got Response(%d:%s)\n",_response->getProtocolType(),typeid(*_response).name()));
        return true;
    }

    return false;
}

void Tetrix::on_action_Create_Room_triggered()
{
    if(_cur_room.length())
        return;

    CreateRoomReq request("test001","123456");
    CreateRoomResp *resp = 0;

    if(doRequest(request)
        && _response.get()
        && (resp = dynamic_cast<CreateRoomResp *>(_response.get()))
        && resp->getProtocolType() == PT_RESP_CREATE_ROOM)
    {
        if(resp == 0 || !resp->success()){
            ACE_DEBUG((LM_DEBUG,"Create Room Fail(%d)\n\n",resp->getReason()));
	}
        else{
            this->_cur_room = "test001";
            ACE_DEBUG((LM_DEBUG,"Create Room OK\n\n"));
        }
    }
    else{   //sock stream error
	_conn.close();
	_conn.set_handle(0);
    }
}

void Tetrix::on_action_Room_List_triggered()
{
    GetRoomListReq request;
    GetRoomListResp *resp = 0;

    if(doRequest(request)
        && _response.get()
        && (resp = dynamic_cast<GetRoomListResp *>(_response.get()))
        && resp->getProtocolType() == PT_RESP_GET_ROOM_LIST)
    {
	resp->print();
        this->_room_map = resp->getRoomsInfo();
        ACE_DEBUG((LM_DEBUG,"Got Room List\n\n"));
    }
    else{   //sock stream error
	_conn.close();
	_conn.set_handle(0);
        ACE_DEBUG((LM_DEBUG,"Recv Room List Fail(%s)\n\n",strerror(errno)));
    }
}

void Tetrix::on_action_Enter_Room_triggered()
{
    if(_cur_room.length())      //already in room
        return;

    std::string room = "test001";
    EnterRoomReq request(room,"123456");
    SimpleProtoSrv *resp = 0;

    if(doRequest(request)
        && _response.get()
        && (resp = dynamic_cast<SimpleProtoSrv *>(_response.get()))
        && resp->getProtocolType() == PT_RESP_ENTER_ROOM)
    {
        long result;
        if(resp->getLong(result,0) && result == (long)E_SUCCESS){
            ACE_DEBUG((LM_DEBUG,"Enter Room OK\n\n"));

            this->_cur_room = room;
        }
        else
            ACE_DEBUG((LM_DEBUG,"Enter Room Fail(%d)\n\n",result));
    }
    else{   //sock stream error
        _conn.close();
        _conn.set_handle(0);
    }
}

void Tetrix::on_action_Leave_Room_triggered()
{
    if(_cur_room.length())
    {
        LeaveRoomReq request(_cur_room);
        SimpleProtoSrv *resp = 0;

        if(doRequest(request)
            && _response.get()
            && (resp = dynamic_cast<SimpleProtoSrv *>(_response.get()))
            && resp->getProtocolType() == PT_RESP_LEAVE_ROOM)
        {
            ACE_DEBUG((LM_DEBUG,"Leave Room OK\n\n"));
            _cur_room = "";
        }
        else{   //sock stream error
            _conn.close();
            _conn.set_handle(0);
        }
    }
}

void Tetrix::addRoom(const std::string &room)
{
    if(!_room_map.count(room)){
        std::pair<quint8,bool> &info = this->_room_map[room];
        info.first = 1;
        info.second = false;
    }
}

void Tetrix::enterRoom(const std::string &room)
{
    if(_room_map.count(room)){
        std::pair<quint8,bool> &info = this->_room_map[room];
        info.first++;
    }
}

void Tetrix::leaveRoom(const std::string &room)
{
    if(_room_map.count(room)){
        std::pair<quint8,bool> &info = this->_room_map[room];
        info.first--;

        if(info.first <= 0)
            _room_map.erase(room);
    }
}

void Tetrix::roomReady(const std::string &room)
{
    if(_room_map.count(room)){
        std::pair<quint8,bool> &info = this->_room_map[room];
        info.second = true;

        if(_cur_room.length() && _cur_room == room)
            on_actionStartGame_triggered();
    }
}

void Tetrix::handleNotify(std::auto_ptr<ProtoSrv> &notify)
{
    //ACE_DEBUG((LM_DEBUG,"Tetrix::handleNotify(%d)\n",notify->getProtocolType(),typeid(*notify).name()));
    switch(notify->getProtocolType())
    {
        case PT_NOTIFY_CREATE_ROOM:
        {
            SimpleProtoSrv *msg = dynamic_cast<SimpleProtoSrv *>(notify.get());
            if(msg){
                std::string room;
                msg->getString(room,0);
                addRoom(room);
            }
        }
        break;

        case PT_NOTIFY_LEAVE_ROOM:
        {
            SimpleProtoSrv *msg = dynamic_cast<SimpleProtoSrv *>(notify.get());
            if(msg){
                std::string room;
                msg->getString(room,0);
                leaveRoom(room);
            }
        }
        break;
        case PT_NOTIFY_GAME_START:
        {
            SimpleProtoSrv *msg = dynamic_cast<SimpleProtoSrv *>(notify.get());
            if(msg){
                std::string room;
                msg->getString(room,0);
                roomReady(room);
            }
        }
        break;
        case PT_NOTIFY_ENTER_ROOM:
        {
            SimpleProtoSrv *msg = dynamic_cast<SimpleProtoSrv *>(notify.get());
            if(msg){
                std::string room;
                msg->getString(room,0);
                enterRoom(room);
            }
        }
        break;

        default:
            break;
    }
}

void *Tetrix::worker(void *arg)
{
    if(arg)
    {
        Tetrix *owner = static_cast<Tetrix *>(arg);
        while(1)
        {
            std::auto_ptr<ProtoSrv> msg(ProtoSrvManager::getInstance()->decode(owner->_conn));
            ACE_DEBUG((LM_DEBUG,"[Worker]Got Data\n"));
            if(msg.get()){
                ACE_DEBUG((LM_DEBUG,"[Worker]Got ProtoSrv(%d:%s)\n",msg->getProtocolType(),typeid(*msg).name()));
                switch(msg->getProtocolType())
                {
                case PT_NOTIFY_CREATE_ROOM:
                case PT_NOTIFY_LEAVE_ROOM:
                case PT_NOTIFY_GAME_START:
                case PT_NOTIFY_ENTER_ROOM:
                    owner->handleNotify(msg);
                    break;
                default:
                    owner->_response = msg;
                    owner->_semaphore.release();
                    break;
                }
            }
            else
                ACE_DEBUG((LM_DEBUG,"[Worker]Got ProtoSrv Fail(%s)\n",strerror(errno)));
        }
    }

    return 0;
}

void Tetrix::on_action_Ready_triggered()
{
    if(_cur_room.empty())
        return;

    StatusSetReq request(true);
    SimpleProtoSrv *resp = 0;

    if(doRequest(request)
        && _response.get()
        && (resp = dynamic_cast<SimpleProtoSrv *>(_response.get()))
        && resp->getProtocolType() == PT_RESP_STATUS_SET)
    {
        long result;
        if(resp->getLong(result,0) && result == (long)E_SUCCESS){
            ACE_DEBUG((LM_DEBUG,"Status Set OK\n\n"));
        }
        else
            ACE_DEBUG((LM_DEBUG,"Status Set Fail(%d)\n\n",result));
    }
    else{   //sock stream error
        _conn.close();
        _conn.set_handle(0);
    }
}
